﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using UnityEngine.UI;

namespace Assets.Scripts.Map
{
    class AStar
    {
        private AStarUnit[,] data;
        public void InitMapData(MapData mdata)
        {
            data = new AStarUnit[mdata.Width, mdata.Height];
            for (int i = 0; i < mdata.Width; i++)
            {
                for (int j = 0; j < mdata.Height; j++)
                {

                }
            }
        }

    }
    /// <summary>
    /// 照着这个的思路，辅以小顶堆实现
    /// https://cloud.tencent.com/developer/article/2391442
    /// </summary>
    public class AStarEnum : IEnumerator<AStarUnit>
    {
        readonly MapData map;
        readonly AStarUnit[,] unitMap;

        public readonly List<V2> list_open = new();
        public readonly List<V2> list_close = new();

        private V2 start;
        private V2 end;

        private V2 current;

        private List<V2> path = new();
        public List<V2> Path
        {
            get { return path; }
        }

        public AStarEnum(MapData mapD, V2 startCoord, V2 endCoord)
        {
            if (!mapD[startCoord])
            {
                throw new Exception("起点有误");
            }

            if (!mapD[endCoord])
            {
                throw new Exception("终点有误");
            }

            map = mapD;
            start = startCoord;
            end = endCoord;

            unitMap = new AStarUnit[mapD.Width, mapD.Height];
            for (int i = 0; i < unitMap.GetLength(0); i++)
            {
                for (int j = 0; j < unitMap.GetLength(1); j++)
                {
                    unitMap[i, j].Pos.X = i;
                    unitMap[i, j].Pos.Y = j;
                }
            }

            current = start;
            unitMap[start.X, start.Y].G = 0;
            unitMap[start.X, start.Y].H = 0;
            list_open.Add(start);
        }

        public AStarUnit Current
        {
            get { return unitMap[current.X, current.Y]; }
        }

        public AStarUnit GetByPos(V2 pos)
        {
            return unitMap[pos.X, pos.Y];
        }

        object IEnumerator.Current => Current;

        public void Dispose()
        {
        }

        //从 open_list 中拿出 f 最小的
        public int FindMinFInOpenList()
        {
            if (list_open.Count == 0)
                throw new Exception("open list 空了，应该在之前就结束了");

            int minIdx = 0;
            int minF = int.MaxValue;
            for (int i = 0; i < list_open.Count; i++)
            {
                V2 coord = list_open[i];
                if (unitMap[coord.X, coord.Y].F < minF)
                {
                    minF = unitMap[coord.X, coord.Y].F;
                    minIdx = i;
                }
            }

            return minIdx;
        }

        //internal bool MoveNext()
        //{
        //    throw new NotImplementedException();
        //}

        public bool MoveNext()
        {
            //
            if (list_open.Count == 0)
            {
                //找不到终点，结束
                path.Clear();
                return false;
            }

            {//从 open_list 中取出 F 最小的作为本轮节点
                int idx = FindMinFInOpenList();
                current = list_open[idx];
                list_open.RemoveAt(idx);
                list_close.Add(current);
            }

            {//遍历周围的点
                for (int x_offset = -1; x_offset <= 1; x_offset++)
                {
                    for (int y_offset = -1; y_offset <= 1; y_offset++)
                    {
                        V2 v = new() { X = current.X + x_offset, Y = current.Y + y_offset };
                        if (v == current) continue;

                        if (v == end)
                        {
                            unitMap[v.X, v.Y].Parent = current;
                            path.Add(v);
                            RecallPath(v);
                            return false;
                        }

                        if (!map[v] || list_close.Contains(v))
                        {
                            //忽略不可达的点
                            //忽略闭表中的点
                            continue;
                        }

                        if (!list_open.Contains(v))
                        {
                            list_open.Add(v);
                            AStarUnit unit = unitMap[v.X, v.Y];
                            AStarUnit cur = unitMap[current.X, current.Y];

                            unit.Parent = current;
                            unit.G = V2.GetG(v, current) + cur.G;
                            unit.H = V2.GetH(v, end);
                            unitMap[v.X, v.Y] = unit;
                        }
                        //如果节点已在 openList 中，并且经过当前节点到达该节点的 G 值更小，则更新该节点的 G 值和父节点指针。
                        else
                        {
                            AStarUnit unit = unitMap[v.X, v.Y];
                            AStarUnit cur = unitMap[current.X, current.Y];
                            int curG = V2.GetG(v, current) + cur.G;
                            if (curG < unit.G)
                            {
                                unit.G = curG;
                                unit.Parent = current;
                                unitMap[v.X, v.Y] = unit;
                            }
                        }
                    }
                }
            }

            return true;
        }

        //从终点回溯路径
        private void RecallPath(V2 pos)
        {
            AStarUnit unit = unitMap[pos.X, pos.Y];
            if (unit.Pos != start)
            {
                RecallPath(unit.Parent);
            }
            path.Add(pos);
        }

        void IEnumerator.Reset()
        {
            current = start;
            unitMap[start.X, start.Y].G = 0;
            unitMap[start.X, start.Y].H = 0;
            list_open.Add(start);
        }
    }

    public struct AStarUnit
    {
        public AStarUnit(V2 pos)
        {
            Pos = pos;
            G = 0;
            H = 0;
            Parent = new V2 { X = -1, Y = -1 };
        }
        public V2 Pos;
        public int G { get; set; }
        /// <summary>
        /// 距离终点的曼哈顿距离
        /// </summary>
        public int H { get; set; }
        public V2 Parent { get; set; }
        public int F
        {
            get { return G + H; }
        }
    }
}
